﻿// Variable group is used to only send data to those in your game room.
var group = null;
var masterHub = null;

var gameRoomModel = null;

// Stores the player list/slots.
var playerList = null;
var playerSlots = null;

// User Profile ID of this client.
var playerId = null;
var playingCPU = false;
var difficulty = null;

// For deciding which player is to start. A random number of 1-100 is rolled and stored.
var playerRolled = null;
var opponantRolled = null;

var playerTurn = false;
var playerStarted = false;

// Tracks whether the client has sent or recieved a Signalr message for rematch.
var rematchSent = false;
var rematchRecieved = false;

// Which player is X and which player is O
var playerMark = null;
var opponantMark = null;

var playerColor = null;
var opponantColor = null;

// Array of arrays of all the possible winning conditions.
var winningConditions = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]];

// Initalizing function that is called with id of gameroom as parameter.
function initializeTicTacToe(id, hub, model, idOfPlayer) {
    group = id;
    masterHub = hub;
    playerList = model.slots();
    playerId = idOfPlayer;
    gameRoomModel = model;
    gameRoomModel.difficulty = ko.observableArray(['Létt', 'Miðlungs', 'Erfitt'])
    gameRoomModel.cpu = ko.observable(false);

    $("#rematchButton").hide();

    // Signalr function. Recieves data with what cell the opponant player marked.
    masterHub.client.cellClicked = function (cellId) {
        // Fills the corresponding cell with an O.
        $("#" + cellId).text(opponantMark).css("color", opponantColor);
        // It's now the clients turn.
        playerTurn = true;
        $("#turn").text("Þú átt að gera").css("color", "green");
        // Checks if the opponant player has won.
        if (HasPlayerWon(opponantMark)) {
            $("#turn").text("Þú tapaðir. Þú ert ömurlegur!").css("color", "red");
            $("#losses").text(parseInt($("#losses").text()) + 1);
            Rematch();
        }
        else if (IsBoardFull()) {
            $("#turn").text("Jafntefli.. enginn vann :(");
            $("#draws").text(parseInt($("#draws").text()) + 1);
            Rematch();
        }
    };

    // Signalr function. Recieves and updated list of players and stores it.
    masterHub.client.updatePlayers = function (players) {
        playerList = $.parseJSON(players);
        // Checks if the player who joined the GameRoom is Computer controlled 
        if (playerList[1].isCPU)
            gameRoomModel.cpu(true);
    };

    // Signalr function. Recieves rematch invitations.
    masterHub.client.rematchClicked = function () {
        // If client has already sent rematch invitation to opponant then we just restart the game.
        if (rematchSent) {
            $("#rematch #rematchHeader").text("Mótherji samþykkti!");
            ResetBoard(true);
        }
        else {
            $("#rematch #rematchHeader").text("Mótherji vill spila aftur. Þorirðu?");
            $("#rematchButton").show();
            rematchRecieved = true;
        }
    };

    // Recieves the number the opponant rolled to decide who should start first.
    masterHub.client.recieveFirst = function (number) {
        opponantRolled = number;
        // If our player has already rolled the dice..
        if (playerRolled != null) {
            $("#turn").before('<h3 id="dice">Þú fékkst ' + playerRolled + ' <br />Mótherji fékk ' + opponantRolled + '</h3>');
            $("#gameStatus #dice").delay(8000).fadeOut('slow');
            if (opponantRolled < playerRolled) {
                playerTurn = true;
                playerStarted = true;
                playerMark = "X";
                playerColor = "blue";
                opponantMark = "O";
                opponantColor = "red";
                $("#turn").text("Þú byrjar :)").css("color", "green");
            }
            else {
                playerTurn = false;
                playerMark = "O";
                playerColor = "red";
                opponantMark = "X";
                opponantColor = "blue";
                $("#turn").text("Mótherji byrjar :(").css("color", "black");
            }
        }
    };

    // Start Game function which is called when both players have clicked "Ready" in the gameroom.
    masterHub.client.startGame = function () {
        ResetBoard(false);
        // Roll a dice from 1-100 to check which player should start.
        playerRolled = 1 + Math.floor(Math.random() * 100);
        // Are we playing the cpu?
        if (playerList[1].isCPU) {
            playingCPU = true;
            // Roll the dice for computer.
            opponantRolled = 1 + Math.floor(Math.random() * 100);
            // Get the difficulty the player selected.
            difficultyText = $("#selectDifficulty").val();
            difficulty = 1 + ($("#selectDifficulty").prop("selectedIndex"));
            $("#gameStatus").prepend("<p id='showDifficulty'>Difficulty: <b>" + difficultyText + "</b></p>");
        }
        else {
            // Sends this random number to the opponant.
            masterHub.server.findFirst(group, playerRolled);
        }
        // If the opponant has already sent us his randomly generated number.
        if (opponantRolled != null) {
            $("#turn").before('<h3 id="dice">Þú fékkst ' + playerRolled + ' <br />Mótherji fékk ' + opponantRolled + '</h3>');
            $("#gameStatus #dice").delay(8000).fadeOut('slow');
            if (playerRolled > opponantRolled) {
                playerTurn = true;
                playerStarted = true;
                playerMark = "X";
                playerColor = "blue";
                opponantMark = "O";
                opponantColor = "red";
                $("#turn").text("Þú byrjar :)").css("color", "green");
            }
            else {
                playerTurn = false
                playerMark = "O";
                opponantMark = "X";
                playerColor = "red";
                opponantColor = "blue";
                $("#turn").text("Mótherji byrjar :(").css("color", "black");
                if (playingCPU)
                    ComputerTurn();
            }
        }
    }

    // Starts the connection with the Signalr server.
    $.connection.hub.start().done(function () {
        // If the client clicked a cell.
        $("#TicTacToe #cells tr td").click(function () {
            // Checks if a player is allowed to move.
            if (CanPlayerMove(this.id, false)) {
                // It's now the opponants turn.
                playerTurn = false;
                $("#turn").text("Mótherji á að gera").css("color", "black");
                // Fills the corresponding cell with an X.
                $(this).text(playerMark).css("color", playerColor);
                // Checks if the client has won.
                if (HasPlayerWon(playerMark)) {
                    $("#turn").text("Þú vannst!! :D").css("color", "green");
                    $("#wins").text(parseInt($("#wins").text()) + 1);
                    // We don't log matches against the CPU!
                    if (!playingCPU)
                    {
                        masterHub.server.endGame(group, gameRoomModel.gameRoomID);
                        masterHub.server.logWin(playerList[0].userProfileID, playerList[1].userProfileID, playerId);
                        masterHub.server.clickCell(group, this.id);
                    }
                    Rematch();
                }
                else if (IsBoardFull()) {
                    $("#turn").text("Jafntefli.. enginn vann :(").  css("color", "black");
                    $("#draws").text(parseInt($("#draws").text()) + 1);
                    // We don't log matches against the CPU!
                    if (!playingCPU)
                    {
                        masterHub.server.endGame(group, gameRoomModel.gameRoomID);
                        masterHub.server.logDraw(playerList[0].userProfileID, playerList[1].userProfileID);
                        masterHub.server.clickCell(group, this.id);
                    }
                    Rematch();
                }
                else if (playingCPU)
                    ComputerTurn();
                else
                    masterHub.server.clickCell(group, this.id);
            }
        });

        // Signalr function that's run when player has pressed rematch button.
        $("#rematch #rematchButton").click(function () {
            if (playingCPU) {
                $("#rematch #rematchHeader").text("Hefjið leik..");
                ResetBoard(true);
            }
            else {
                // Send rematch invite through signalr.
                masterHub.server.clickRematch(group);

                // If the client has already recieved a rematch invitation from opponant we just restart the game.
                if (rematchRecieved) {
                    $("#rematch #rematchHeader").text("Hefjið leik..");
                    ResetBoard(true);
                }
                else {
                    $("#rematch #rematchHeader").text("Bíð eftir svari frá mótherja..");
                    $("#rematchButton").hide();
                    rematchSent = true;
                }
            }
        });
    });
}

/* Function that returns true if a player is allowed to move. Checks all the necessary conditions. 
##Checks##
1. If it's the players turn
2. If the cell is already marked
3. If the board is full
4. If player X has won
5. If player O has won */
function CanPlayerMove(cellClicked) {
    var array = GetArrayOfCells();
    if (!playerTurn) {
        return false;
    }
    else if (array[cellClicked] === playerMark || array[cellClicked] === opponantMark) {
        return false;
    }
    else if (!IsBoardFull) {
        return false;
    }
    else if (HasPlayerWon(playerMark)) {
        return false;
    }
    else if (HasPlayerWon(opponantMark)) {
        return false;
    }
    return true;
}

// Function that returns an array of the cells on the board.
function GetArrayOfCells() {
    var myTableArray = [];
    // Iterates through all the cells of the board and adds it to the array.
    $('#TicTacToe #cells tr td').each(function () {
        myTableArray.push($(this).text());
    });
    return myTableArray;
}

// Checks whether a board is full.
function IsBoardFull() {
    var cells = GetArrayOfCells();
    for (var i = 0; i < 9; i++) {
        // If a single cell on the board is empty then the board is obviously not full.
        if (cells[i] !== playerMark && cells[i] !== opponantMark) {
            return false;
        }
    }
    return true;
}

// Checks if a player has won. Recieves a mark of the players as a parameter and only checks if that player has won.
function HasPlayerWon(mark) {
    var cells = GetArrayOfCells();
    // Iterates through the arrays of possible winning conditions.
    for (var i = 0; i < 8; i++) {
        // If our array of cells has our mark in all the indexes this specific winningCondition describes the player has won.
        if (cells[winningConditions[i][0]] === mark && cells[winningConditions[i][1]] === mark && cells[winningConditions[i][2]] === mark) {
            return true;
        }
    }
    return false;
}

// Function that displays rematch button and message at the end of game  // moved endGame call down ( do not want to call it two times / by each user )
function Rematch()
{
    $("#rematchButton").show();
    $("#rematch #rematchHeader").text("Viltu spila aftur?").show();
}

// Resets our board and reinitializes all our stuff.
function ResetBoard(rematch) {
    $('#TicTacToe #cells tr td').each(function () {
        $(this).text("");
    });
    // If we are Resetting the Board because of a Rematch.
    if (rematch) {
        rematchSent = false;
        rematchRecieved = false;
        $("#rematchButton").hide();
        $("#rematch #rematchHeader").delay(5000).fadeOut('slow');
        if (playerStarted) {
            $("#turn").text("Mótherji á að gera").css("color", "black");
            playerStarted = false;
            playerTurn = false;
            playerMark = "O";
            playerColor = "red";
            opponantMark = "X";
            opponantColor = "blue";
            if (playingCPU)
                ComputerTurn();
        }
        else {
            $("#turn").text("Þú átt að gera").css("color", "green");
            playerStarted = true;
            playerTurn = true;
            playerMark = "X";
            playerColor = "blue";
            opponantMark = "O";
            opponantColor = "red";
        }
    }
}

// AI for computer.
function ComputerTurn() {
    var cellsArray = GetArrayOfCells();
    var cellToSelect = null;

    switch (difficulty) {
        // Easy difficulty
        case 1:
            // We just select a random cell until we find one that's empty
            do {
                cellToSelect = (1 + Math.floor(Math.random() * 9) - 1);
            } while (cellsArray[cellToSelect] === playerMark || cellsArray[cellToSelect] === opponantMark)
            break;
        // Medium difficulty
        case 2:
            // First the CPU checks whether it is able to win with one click
            cellToSelect = IsAbleToWin(opponantMark);

            // Next she checks whether the opponant can win
            if (cellToSelect == null)
                cellToSelect = IsAbleToWin(playerMark);

            // Then she tries to select the cell in the middle
            if (cellToSelect == null && cellsArray[4].length == 0) {
                cellToSelect = 4;
            }

            // Then she chooses a random cell
            if (cellToSelect == null) {
                do {
                    cellToSelect = (1 + Math.floor(Math.random() * 9) - 1);
                } while (cellsArray[cellToSelect] === playerMark || cellsArray[cellToSelect] === opponantMark)
            }
            break;
        // Hard difficulty
        case 3:
            // Is CPU able to win?
            cellToSelect = IsAbleToWin(opponantMark);

            // Is opponant able to win?
            if (cellToSelect == null)
                cellToSelect = IsAbleToWin(playerMark);

            // Is CPU able to create a fork that lead's to sure victory?
            if (cellToSelect == null)
                cellToSelect = CheckForFork(opponantMark);

            // Is opponant able to create a fork?
            if (cellToSelect == null)
                cellToSelect = CheckForFork(playerMark);

            // Is middle cell selectable?
            if (cellToSelect == null && cellsArray[4].length == 0) {
                cellToSelect = 4;
            }

            // Are the corner cells selectable?
            if (cellToSelect == null) {
                // Array that contains the index position of the corners
                var corners = [0, 2, 6, 8];
                // Array that contains the index position of each corner's opposite corner
                var oppositeCorner = [8, 6, 2, 0];
                for (var i = 0; i < 4; i++) {
                    // If the opponant has selected a corner, we select the opposite corner
                    if (cellsArray[corners[i]] === playerMark && cellsArray[oppositeCorner[i]].length == 0) {
                        cellToSelect = oppositeCorner[i];
                    }
                }
                // Since the opponant hasn't selected a corner we just select a random corner
                if (cellToSelect == null) {
                    for (var i = 0; i < 4; i++) {
                        if (cellsArray[corners[i]].length == 0) {
                            cellToSelect = corners[i];
                        }
                    }
                }
            }

            // We select a random cell since all of the above failed
            if (cellToSelect == null) {
                do {
                    cellToSelect = (1 + Math.floor(Math.random() * 9) - 1);
                } while (cellsArray[cellToSelect] === playerMark || cellsArray[cellToSelect] === opponantMark)
            }
            break;
    }
    // We create a random interval to simulate that the CPU is "thinking"
    var randomInterval = 500 + Math.floor(Math.random() * 700);
    setTimeout(function () { masterHub.client.cellClicked(cellToSelect) }, randomInterval)
    playerTurn = true;
}

// Checks if a mark has a winning scenario where it can select a single cell to win
function IsAbleToWin(mark) {
    var cellsArray = GetArrayOfCells();
    var cellToSelect = null;
    // We iterate through the 8 possible winning conditions
    for (var i = 0; i < 8; i++) {
        // And check each permutation of each winning condition whether 2 cells have the mark in a and that the 3rd cell is empty
        if (cellsArray[winningConditions[i][0]] === mark && cellsArray[winningConditions[i][1]] === mark && cellsArray[winningConditions[i][2]].length == 0) {
            cellToSelect = winningConditions[i][2];
        }
        else if (cellsArray[winningConditions[i][0]] === mark && cellsArray[winningConditions[i][2]] === mark && cellsArray[winningConditions[i][1]].length == 0) {
            cellToSelect = winningConditions[i][1];
        }
        else if (cellsArray[winningConditions[i][1]] === mark && cellsArray[winningConditions[i][2]] === mark && cellsArray[winningConditions[i][0]].length == 0) {
            cellToSelect = winningConditions[i][0];
        }
    }
    return cellToSelect;
}

// Are we able to create a fork? A fork is when a player can assure victory in his next turn by creating 2 possible winning scenarios at the same time
function CheckForFork(mark) {
    var cellsArray = GetArrayOfCells();
    var counter;
    // We iterate through all the cells
    for (var i = 0; i < 9; i++) {
        if (cellsArray[i].length == 0) {
            cellsArray[i] == mark;
            // We check how many winning conditions there will be if we mark this cell. If it's 2 or more we have a fork and return the cell
            if (WinningScenarios(mark, cellsArray) > 1) {
                return i;
            }
            cellsArray[i] == "";
        }
    }
    // Wasn't forkable
    return null;
}

// We check how many winning scenarios the board has.
function WinningScenarios(mark, cellsArray) {
    var counter = 0;
    // We iterate through all the 8 winning conditions.
    for (var i = 0; i < 8; i++) {
        // And check each permutation of each winning condition whether 2 cells have the mark in a and that the 3rd cell is empty
        if (cellsArray[winningConditions[i][0]] === mark && cellsArray[winningConditions[i][1]] === mark && cellsArray[winningConditions[i][2]].length == 0) {
            // For each winning condition that can be fulfilled, we increment the counter.
            counter++;
        }
        else if (cellsArray[winningConditions[i][0]] === mark && cellsArray[winningConditions[i][2]] === mark && cellsArray[winningConditions[i][1]].length == 0) {
            counter++;
        }
        else if (cellsArray[winningConditions[i][1]] === mark && cellsArray[winningConditions[i][2]] === mark && cellsArray[winningConditions[i][0]].length == 0) {
            counter++;
        }
    }
    return counter;
}